package com.example.Filter;

import com.example.common.Result;
import com.example.entity.LoginUser;
import com.example.utils.JwtUtil;
import com.example.utils.RedisCache;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Objects;

/**
 * jwt认证过滤器
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private RedisCache redisCache;
    private final ObjectMapper objectMapper;
 public  static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);

    public JwtAuthenticationTokenFilter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        // 获取请求路径
        String requestUri = request.getRequestURI();

        // 检查请求路径是否允许未认证用户访问
        boolean isPublicEndpoint = "/register".equals(requestUri) || "/login".equals(requestUri) || "/test".equals(requestUri);

        if (isPublicEndpoint) {
            // 如果是公开端点，则直接放行
            filterChain.doFilter(request, response);
        } else {
            // 否则，进行JWT认证
            String token = request.getHeader("token");
            String userId = null;

            if (!StringUtils.hasText(token)) {
                // 如果请求头没有token，则返回用户未登录的消息
                logger.info("Token is missing in the request header用户未登录");
                sendResultResponse(response,Result.authenticationFailure( "用户还没有登录"));
                return; // 结束方法执行，不再继续过滤器链
            }

            // 如果token不为空，则解析token获取用户id
            try {
                Claims claims = JwtUtil.parseJWT(token);
                userId = claims.getSubject();
            } catch (Exception e) {
                logger.error("Invalid token: ", e);
                sendResultResponse(response,Result.authenticationFailure( "用户连接超时，请重新登录"));
                return;
            }

            // 从redis中获取用户信息
            String redisKey = "login" + userId;
            LoginUser loginUser = redisCache.getCacheObject(redisKey);
            logger.info("用户"+loginUser.getUser().getName()+"已登录");

            if (Objects.nonNull(loginUser)) {
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        loginUser, null, null
                );
                // 存入SecurityContextHolder，用于退出登录
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                // 如果一切正常，则继续过滤器链
                filterChain.doFilter(request, response);
            } else {
                logger.warn("用户未登录，User not found in Redis for token: " + token);
                sendResultResponse(response,Result.error( "用户还没有登录"));
                return;
            }
        }
    }

    /**
     * token无效时给前端响应
     * @param response
     * @throws IOException
     */
    private void sendResultResponse(HttpServletResponse response, Result result) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonResponse = objectMapper.writeValueAsString(result);
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(jsonResponse);
    }
}
